#ifndef SM3_HEAD
#define SM3_HEAD

// #include<stdint.h>
#include "stdint.h"

// 全局常量和结构体
// 字长
#define WORD_LEN 32

/**
 * SM3的上下文结构体.
 */
typedef struct
{
    uint32_t state[8];  // 摘要值。初始值 IV = 7380166F 4914B2B9 172442D7 DA8A0600 A96F30BC 163138AA E38DEE4D B0FB0E4E
    uint8_t buffer[64]; // 缓冲区。每一个块的大小是 512 比特
    size_t len;         // 需要进行哈希的所有数据的长度。大小size_t = uint64_t
} sm3_context;

/*
 * 64-bit integer manipulation macros (big endian). int 32位
 */
#define GET_UINT_BE(N, B, I)                                                                                                                          \
    {                                                                                                                                                 \
        (N) = ((unsigned int)(B)[(I)] << 24) | ((unsigned int)(B)[(I) + 1] << 16) | ((unsigned int)(B)[(I) + 2] << 8) | ((unsigned int)(B)[(I) + 3]); \
    }

#define PUT_UINT_BE(N, B, I)                       \
    {                                              \
        (B)[(I)] = (unsigned char)((N) >> 24);     \
        (B)[(I) + 1] = (unsigned char)((N) >> 16); \
        (B)[(I) + 2] = (unsigned char)((N) >> 8);  \
        (B)[(I) + 3] = (unsigned char)((N));       \
    }

/*
 * 64-bit integer manipulation macros (big endian). long 64位
 */
#define GET_ULONG_BE(N, B, I)                                                                                                                                \
    {                                                                                                                                                        \
        (N) = ((unsigned int)(B)[(I)] << 56) | ((unsigned int)(B)[(I) + 1] << 48) | ((unsigned int)(B)[(I) + 2] << 40) | ((unsigned int)(B)[(I) + 3] << 32); \
        | ((unsigned int)(B)[(I) + 4] << 24) | ((unsigned int)(B)[(I) + 5] << 16) | ((unsigned int)(B)[(I) + 6] << 8) | ((unsigned int)(B)[(I) + 7]);        \
    }

#define PUT_ULONG_BE(N, B, I)                      \
    {                                              \
        (B)[(I)] = (unsigned char)((N) >> 56);     \
        (B)[(I) + 1] = (unsigned char)((N) >> 48); \
        (B)[(I) + 2] = (unsigned char)((N) >> 40); \
        (B)[(I) + 3] = (unsigned char)((N) >> 32); \
        (B)[(I) + 4] = (unsigned char)((N) >> 24); \
        (B)[(I) + 5] = (unsigned char)((N) >> 16); \
        (B)[(I) + 6] = (unsigned char)((N) >> 8);  \
        (B)[(I) + 7] = (unsigned char)((N));       \
    }

/**
 * SM3上下文初始化函数.
 *
 * \param ctx 上下文
 * \return 返回1表示正确
 */
int sm3_init(sm3_context *ctx);

// 1.算法基础
// 1.1.初始值
#define SM3_A 0x7380166fUL
#define SM3_B 0x4914b2b9UL
#define SM3_C 0x172442d7UL
#define SM3_D 0xda8a0600UL
#define SM3_E 0xa96f30bcUL
#define SM3_F 0x163138aaUL
#define SM3_G 0xe38dee4dUL
#define SM3_H 0xb0fb0e4eUL

// 1.2.常量
#define SM3_CONST_T0 0x79CC4519
#define SM3_CONST_T1 0x7A879D8A

// 1.3.布尔函数
// FF0 0<=j<=15
#ifndef FF0
#define FF0(X, Y, Z) ((X) ^ (Y) ^ (Z))
#endif

// FF1 15<=j<=63
#ifndef FF1
#define FF1(X, Y, Z) (((X) & (Y)) | ((X) & (Z)) | ((Y) & (Z)))
#endif

// GG0 0<=j<=15
#define GG0(X, Y, Z) ((X) ^ (Y) ^ (Z))
// GG1 15<=j<=63
#define GG1(X, Y, Z) ((X) & (Y) | ((~X) & (Z)))

// 1.4.置换函数
// 循环左移函数
// #define ROTATE(X, N) ((X << N) | ((X & 0xFFFFFFFF) >> (WORD_LEN - N))
#define ROTATE(X, N) (((X) << (N)) | ((X & 0xFFFFFFFF) >> (WORD_LEN - N)))

// P0 = X ^ (X <<< 9) ^ (X <<< 17)
#define P0(X) (X ^ ROTATE(X, 9) ^ ROTATE(X, 17))
// P1 = X ^ (X <<< 15) ^ (X <<< 23)
#define P1(X) (X ^ ROTATE(X, 15) ^ ROTATE(X, 23))

// 2.消息压缩
// 2.1.压缩函数 涉及 消息扩展
// 消息扩展
#define EXPAND(W0, W7, W13, W3, W10) \
    (P1(W0 ^ W7 ^ ROTATE(W13, 15)) ^ ROTATE(W3, 7) ^ W10)

/**
 * SM3的压缩函数。将Vi和Bi压缩成V(i+1).
 *
 * \param ctx 上下文
 * \param data Bi。消息填充并按512个比特进行分块。Bi表示其中的第i块。
 * \return 返回1表示正确
 */
int sm3_compress_function(sm3_context *ctx, const uint8_t data[64]);

// 2.2.迭代函数 涉及消息填充

/**
 * 迭代函数.
 *
 * \param ctx 上下文
 * \param input 输入
 * \param input_len 输入长度
 * \return 返回1表示正确
 */
int sm3_update(sm3_context *ctx, uint8_t *input, const size_t input_len);

// 填充值
static const uint8_t sm3_padding[64] =
    {
        0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

// 2.3.获取最后的摘要值
/**
 * 处理最后一个块，并获取最终的摘要值。最后一个块 = 未处理完的消息 + 填充值 + 消息长度.
 *
 * \param output 输出
 * \param ctx 上下文
 * \return 返回1表示正确
 */
int sm3_finish(uint8_t output[32], sm3_context *ctx);

// 3.其他
/**
 * 使用16进制的格式打印字符数组.
 *
 * \param desc 说明
 * \param chars 字符数组
 * \param len 字符数组的长度
 */
void print_chars_by_hex(const int8_t desc[], const uint8_t chars[], const size_t len);

/**
 * 使用16进制的格式打印 uint32 数组.
 *
 * \param desc 说明
 * \param arr uint32_t 数组
 * \param len uint32_t 数组的长度
 */
void print_uint32_arr_by_hex(const int8_t desc[], const uint32_t arr[], const size_t len);

#endif